using System;
using System.Collections;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
using System.Runtime.Remoting;

namespace Meta
{
	public class Utilities {
		public static bool IsRemote(object x) {
			return x!=null && RemotingServices.IsTransparentProxy(x);
		}

		public static object[] Arglist(object o) {
			if (IsRemote(o))
				return null;
			IArglist i = o as IArglist;
			if (i==null)
				return null;
			else
				return i.Arglist;
		}

		public static string Documentation(object o) {
			if (IsRemote(o))
				return null;
			IDocumentable i = o as IDocumentable;
			if (i==null)
				return null;
			else
				return i.Documentation;
		}

		public static string StripMemberReferences(string s) {
			int dot = s.IndexOf('.');
			if (dot<0)
				return s;
			else
				return s.Substring(0,dot);
		}

		public static bool IsNumber(object o) {
			switch(System.Convert.GetTypeCode(o)) {
				case System.TypeCode.Byte:
				case System.TypeCode.Decimal:
				case System.TypeCode.Double:
				case System.TypeCode.Int16:
				case System.TypeCode.Int32:
				case System.TypeCode.Int64:
				case System.TypeCode.SByte:
				case System.TypeCode.Single:
				case System.TypeCode.UInt16:
				case System.TypeCode.UInt32:
				case System.TypeCode.UInt64:
					return true;

				default:
					return false;
			}
		}

		// It seems crazy that I have to do this.
		public static Type ArrayTypeOf(Type t) {
			if (t==typeof(object))
				return typeof(IList);
			else
				return Array.CreateInstance(t,0).GetType();
		}

		public static string SubstituteNewLines(string s) {
			return s.Replace("\\n","\n");
		}

		public static object CoerceToType(Type t, object v) {
			if (t.IsEnum)   // Otherwise Enums get changed to their base types
				return v;

			TypeCode c = Type.GetTypeCode(t);
			switch (c) {
				case TypeCode.Object:
				case TypeCode.Boolean:
				case TypeCode.DateTime:
				case TypeCode.DBNull:
				case TypeCode.Empty:
				case TypeCode.String:
					return v;

				default:
					return System.Convert.ChangeType(v, c);
			}
		}

		public static bool IsNumber(object a, object b) {
			return IsNumber(a) && IsNumber(b);
		}
		public static bool IsNumber(object a, object b, object c) {
			return IsNumber(a) && IsNumber(b) && IsNumber(c);
		}		
		public static bool IsNumber(object a, object b, object c, object d) {
			return IsNumber(a) && IsNumber(b) && IsNumber(c) && IsNumber(d);
		}		
		public static bool IsNumber(object a, object b, object c, object d, object e) {
			return IsNumber(a) && IsNumber(b) && IsNumber(c) && IsNumber(d) && IsNumber(e);
		}
		public static bool IsNumber(object a, object b, object c, object d, object e, object f) {
			return IsNumber(a) && IsNumber(b) && IsNumber(c) && IsNumber(d) && IsNumber(e) && IsNumber(f);
		}

		public static bool IsIntegral(object o) {
			return IsInteger(o) || o is System.Enum;
		}
												 
		public static bool IsInteger(object o) {
			switch(System.Convert.GetTypeCode(o)) {
				case System.TypeCode.Byte:
				case System.TypeCode.Int16:
				case System.TypeCode.Int32:
				case System.TypeCode.Int64:
				case System.TypeCode.SByte:
				case System.TypeCode.UInt16:
				case System.TypeCode.UInt32:
				case System.TypeCode.UInt64:
					return true;

				default:
					return false;
			}
		}
		public static bool IsInteger(object a, object b) {
			return IsInteger(a) && IsInteger(b);
		}
		public static bool IsInteger(object a, object b, object c) {
			return IsInteger(a) && IsInteger(b) && IsInteger(c);
		}		
		public static bool IsInteger(object a, object b, object c, object d) {
			return IsInteger(a) && IsInteger(b) && IsInteger(c) && IsInteger(d);
		}		
		public static bool IsInteger(object a, object b, object c, object d, object e) {
			return IsInteger(a) && IsInteger(b) && IsInteger(c) && IsInteger(d) && IsInteger(e);
		}
		public static bool IsInteger(object a, object b, object c, object d, object e, object f) {
			return IsInteger(a) && IsInteger(b) && IsInteger(c) && IsInteger(d) && IsInteger(e) && IsInteger(f);
		}

		public static bool NumberIsInteger(object o) {
			switch(System.Convert.GetTypeCode(o)) {
				case System.TypeCode.Byte:
				case System.TypeCode.Int16:
				case System.TypeCode.Int32:
				case System.TypeCode.Int64:
				case System.TypeCode.SByte:
				case System.TypeCode.UInt16:
				case System.TypeCode.UInt32:
				case System.TypeCode.UInt64:
					return true;

				case System.TypeCode.Decimal:
				case System.TypeCode.Double:
				case System.TypeCode.Single:
					return false;

				default:
					throw new ArgumentTypeException("number", o, PseudoType.Number);
			}
		}

		// NOTE: non-shortcircuiting & is deliberate here because we want to type-check all the args regardless of the return result
		public static bool NumberIsInteger(object a, object b) {
			return NumberIsInteger(a) & NumberIsInteger(b);
		}
		public static bool NumberIsInteger(object a, object b, object c) {
			return NumberIsInteger(a) & NumberIsInteger(b) & NumberIsInteger(c);
		}		
		public static bool NumberIsInteger(object a, object b, object c, object d) {
			return NumberIsInteger(a) & NumberIsInteger(b) & NumberIsInteger(c) & NumberIsInteger(d);
		}		
		public static bool NumberIsInteger(object a, object b, object c, object d, object e) {
			return NumberIsInteger(a) & NumberIsInteger(b) & NumberIsInteger(c) & NumberIsInteger(d) & NumberIsInteger(e);
		}
		public static bool NumberIsInteger(object a, object b, object c, object d, object e, object f) {
			return NumberIsInteger(a) & NumberIsInteger(b) & NumberIsInteger(c) & NumberIsInteger(d) & NumberIsInteger(e) & NumberIsInteger(f);
		}

		public static bool IsFloat(object o) {
			switch(System.Convert.GetTypeCode(o)) {
				case System.TypeCode.Decimal:
				case System.TypeCode.Double:
				case System.TypeCode.Single:
					return true;

				default:
					return false;
			}
		}

		
		public static int ToInt32(string s) {
			return System.Convert.ToInt32(s.Replace("\u2212","-"));
		}
		public static double ToDouble(string s) {
			return System.Convert.ToDouble(s.Replace("\u2212","-"));
		}
		public static Type SearchForType(string name) {
			Type t = Type.GetType(name);
			bool isQualified = name.IndexOf('.')>=0;
			if (t!=null)
				return t;
			else {
				// No more Mr. Nice Guy.
				foreach (System.Reflection.Assembly a in AppDomain.CurrentDomain.GetAssemblies()) {
					//t = a.GetType(name);
					//if (t!= null)
					//	return t;
					if (isQualified) {
#if TYPESEARCH_HARDWAY
						foreach (Type t2 in a.GetTypes()) {
							if (t2.FullName == name)
								return t2;
						}
#else
						t = a.GetType(name); if (t!=null) return t; }
#endif
					else
						foreach (Type t2 in a.GetTypes()) {
							if (t2.Name == name)
								return t2;
						}
				}
			}
			return null;
		}

		public static ArrayList namespaceSearchPath = new ArrayList();

		static bool ImportingFromNamespace(string name) {
			foreach (string ns in namespaceSearchPath)
				if (ns==name)
					return true;
			return false;
		}

		public static void AddNamespaceSearchPath(object[] path) {
			for (int i = 0; i<path.Length; i++) {
				object o = path[i];
				string name=null;

				if (o is Symbol)
					name = ((Symbol)o).name;
				else if (o is String)
					name = (string)o;
				else
					throw new ArgumentTypeException("namespace", o, typeof(string));
				if (!ImportingFromNamespace(name))
					namespaceSearchPath.Insert(0, name);
			}
		}

		public static bool IsVisible(Type t) {
			string name = t.FullName;
			string littlename = t.Name;

			foreach (string ns in namespaceSearchPath)
				if (name == (ns+"."+littlename))
					return true;

			return false;
		}

#if notdef
		public static object MaybeImportVariable(GlobalVariable v) {
			string typeName = v.name.name;
			
			foreach (System.Reflection.Assembly a in AppDomain.CurrentDomain.GetAssemblies())
				if (!(a is AssemblyBuilder)) {
					foreach (Type t in a.GetTypes())
						if (t.Name==typeName)
							// Right name.  See if it's in our search path.
							foreach (string ns in namespaceSearchPath) 
								if (t.FullName==(ns+"."+typeName)) {
									v.value = t;
									v.isDefined = true;
									return t;
								}
				}

			throw new UndefinedVariableException(v);
		}
#endif

		public static MemberInfo[] GetMembers(Type t, string name) {
			return t.GetMember(name, MemberTypes.Method | MemberTypes.Field | MemberTypes.Property, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
		}

		public static bool TreeEquivalent(object a, object b) {
			IList aList = a as IList;
			if (aList == null) {
				if (Equivalent(a, b))
					return true;
				else if (a==null || b==null)
					return a==b;
				else {
					Type ta = a.GetType();
					if (ta.IsInstanceOfType(b))
						return (bool)ta.InvokeMember("Equals", BindingFlags.Instance|BindingFlags.Public|BindingFlags.InvokeMethod, null, a, new object[1] {b});
					else {
						Type tb = b.GetType();
						if (tb.IsInstanceOfType(a))
							return (bool)tb.InvokeMember("Equals", BindingFlags.Instance|BindingFlags.Public|BindingFlags.InvokeMethod, null, b, new object[1] {a});
						else
							return false;
					}
				}
			} else {
				IList bList = b as IList;
				if (bList == null)
					return false;
				else {
					int length = aList.Count;
					if (bList.Count != length)
						return false;
					else for (int i=0; i<length; i++)
							 if (!TreeEquivalent(aList[i], bList[i]))
								 return false;
					return true;
				}
			}
		}

		public static bool Equivalent(object a, object b) {
			if (a==null)
				return b==null;
			if (b==null)
				return false;

			TypeCode aType = Type.GetTypeCode(a.GetType());
			TypeCode bType = Type.GetTypeCode(b.GetType());

			switch (aType) {
				case TypeCode.Int32:
				switch (bType) {
					case TypeCode.Int32:
						return (int)a==(int)b;
					case TypeCode.Single:
						return (int)a==(float)b;
					case TypeCode.Double:
						return (int)a==(double)b;
					case TypeCode.Byte:
						return (int)a==(byte)b;
					case TypeCode.Decimal:
						return (int)a==(decimal)b;
					case TypeCode.Int16:
						return (int)a==(System.Int16)b;
					case TypeCode.Int64:
						return (int)a==(System.Int64)b;
					case TypeCode.SByte:
						return (int)a==(System.SByte)b;
					case TypeCode.UInt16:
						return (int)a==(System.UInt16)b;
					case TypeCode.UInt32:
						return (int)a==(System.UInt32)b;
					case TypeCode.UInt64:
						return (int)a>=0 &&(System.UInt64)((int)a)==(System.UInt64)b;
					default:
						return false;
				}
				case TypeCode.Single:
				switch (bType) {
					case TypeCode.Int32:
						return (float)a==(int)b;
					case TypeCode.Single:
						return (float)a==(float)b;
					case TypeCode.Double:
						return (float)a==(double)b;
					case TypeCode.Byte:
						return (float)a==(byte)b;
					case TypeCode.Decimal:
						return (float)a==(float)((decimal)b);
					case TypeCode.Int16:
						return (float)a==(System.Int16)b;
					case TypeCode.Int64:
						return (float)a==(System.Int64)b;
					case TypeCode.SByte:
						return (float)a==(System.SByte)b;
					case TypeCode.UInt16:
						return (float)a==(System.UInt16)b;
					case TypeCode.UInt32:
						return (float)a==(System.UInt32)b;
					case TypeCode.UInt64:
						return (float)a==(System.UInt64)b;
					default:
						return false;
				}
				case TypeCode.Double:
				switch (bType) {
					case TypeCode.Int32:
						return (double)a==(int)b;
					case TypeCode.Single:
						return (double)a==(float)b;
					case TypeCode.Double:
						return (double)a==(double)b;
					case TypeCode.Byte:
						return (double)a==(byte)b;
					case TypeCode.Decimal:
						return (double)a==(double)((decimal)b);
					case TypeCode.Int16:
						return (double)a==(System.Int16)b;
					case TypeCode.Int64:
						return (double)a==(System.Int64)b;
					case TypeCode.SByte:
						return (double)a==(System.SByte)b;
					case TypeCode.UInt16:
						return (double)a==(System.UInt16)b;
					case TypeCode.UInt32:
						return (double)a==(System.UInt32)b;
					case TypeCode.UInt64:
						return (double)a==(System.UInt64)b;
					default:
						return false;
				}
				case TypeCode.Byte:
				switch (bType) {
					case TypeCode.Int32:
						return (byte)a==(int)b;
					case TypeCode.Single:
						return (byte)a==(float)b;
					case TypeCode.Double:
						return (byte)a==(double)b;
					case TypeCode.Byte:
						return (byte)a==(byte)b;
					case TypeCode.Decimal:
						return (byte)a==(decimal)b;
					case TypeCode.Int16:
						return (byte)a==(System.Int16)b;
					case TypeCode.Int64:
						return (byte)a==(System.Int64)b;
					case TypeCode.SByte:
						return (byte)a==(System.SByte)b;
					case TypeCode.UInt16:
						return (byte)a==(System.UInt16)b;
					case TypeCode.UInt32:
						return (byte)a==(System.UInt32)b;
					case TypeCode.UInt64:
						return (byte)a>=0 && (byte)a==(System.UInt64)b;
					default:
						return false;
				}
				case TypeCode.Decimal:
				switch (bType) {
					case TypeCode.Int32:
						return (decimal)a==(int)b;
					case TypeCode.Single:
						return (float)((decimal)a)==(float)b;
					case TypeCode.Double:
						return (double)((decimal)a)==(double)b;
					case TypeCode.Byte:
						return (decimal)a==(byte)b;
					case TypeCode.Decimal:
						return (decimal)a==(decimal)b;
					case TypeCode.Int16:
						return (decimal)a==(System.Int16)b;
					case TypeCode.Int64:
						return (decimal)a==(System.Int64)b;
					case TypeCode.SByte:
						return (decimal)a==(System.SByte)b;
					case TypeCode.UInt16:
						return (decimal)a==(System.UInt16)b;
					case TypeCode.UInt32:
						return (decimal)a==(System.UInt32)b;
					case TypeCode.UInt64:
						return (decimal)a==(System.UInt64)b;
					default:
						return false;
				}
				case TypeCode.Int16:
				switch (bType) {
					case TypeCode.Int32:
						return (System.Int16)a==(int)b;
					case TypeCode.Single:
						return (System.Int16)a==(float)b;
					case TypeCode.Double:
						return (System.Int16)a==(double)b;
					case TypeCode.Byte:
						return (System.Int16)a==(byte)b;
					case TypeCode.Decimal:
						return (System.Int16)a==(decimal)b;
					case TypeCode.Int16:
						return (System.Int16)a==(System.Int16)b;
					case TypeCode.Int64:
						return (System.Int16)a==(System.Int64)b;
					case TypeCode.SByte:
						return (System.Int16)a==(System.SByte)b;
					case TypeCode.UInt16:
						return (System.Int16)a==(System.UInt16)b;
					case TypeCode.UInt32:
						return (System.Int16)a==(System.UInt32)b;
					case TypeCode.UInt64:
						return (System.Int16)a>=0 && (System.UInt64)((System.Int16)a)==(System.UInt64)b;
					default:
						return false;
				}
				case TypeCode.Int64:
				switch (bType) {
					case TypeCode.Int32:
						return (System.Int64)a==(int)b;
					case TypeCode.Single:
						return (System.Int64)a==(float)b;
					case TypeCode.Double:
						return (System.Int64)a==(double)b;
					case TypeCode.Byte:
						return (System.Int64)a==(byte)b;
					case TypeCode.Decimal:
						return (System.Int64)a==(decimal)b;
					case TypeCode.Int16:
						return (System.Int64)a==(System.Int16)b;
					case TypeCode.Int64:
						return (System.Int64)a==(System.Int64)b;
					case TypeCode.SByte:
						return (System.Int64)a==(System.SByte)b;
					case TypeCode.UInt16:
						return (System.Int64)a==(System.UInt16)b;
					case TypeCode.UInt32:
						return (System.Int64)a==(System.UInt32)b;
					case TypeCode.UInt64:
						return (System.Int64)a==(System.Int64)((System.UInt64)b);
					default:
						return false;
				}
				case TypeCode.SByte:
				switch (bType) {
					case TypeCode.Int32:
						return (System.SByte)a==(int)b;
					case TypeCode.Single:
						return (System.SByte)a==(float)b;
					case TypeCode.Double:
						return (System.SByte)a==(double)b;
					case TypeCode.Byte:
						return (System.SByte)a==(byte)b;
					case TypeCode.Decimal:
						return (System.SByte)a==(decimal)b;
					case TypeCode.Int16:
						return (System.SByte)a==(System.Int16)b;
					case TypeCode.Int64:
						return (System.SByte)a==(System.Int64)b;
					case TypeCode.SByte:
						return (System.SByte)a==(System.SByte)b;
					case TypeCode.UInt16:
						return (System.SByte)a==(System.UInt16)b;
					case TypeCode.UInt32:
						return (System.SByte)a==(System.UInt32)b;
					case TypeCode.UInt64:
						return (System.UInt64)((System.SByte)a)==(System.UInt64)b;
					default:
						return false;
				}
				case TypeCode.UInt32:
				switch (bType) {
					case TypeCode.Int32:
						return (System.UInt32)a==(int)b;
					case TypeCode.Single:
						return (System.UInt32)a==(float)b;
					case TypeCode.Double:
						return (System.UInt32)a==(double)b;
					case TypeCode.Byte:
						return (System.UInt32)a==(byte)b;
					case TypeCode.Decimal:
						return (System.UInt32)a==(decimal)b;
					case TypeCode.Int16:
						return (System.UInt32)a==(System.Int16)b;
					case TypeCode.Int64:
						return (System.UInt32)a==(System.Int64)b;
					case TypeCode.SByte:
						return (System.UInt32)a==(System.SByte)b;
					case TypeCode.UInt16:
						return (System.UInt32)a==(System.UInt16)b;
					case TypeCode.UInt32:
						return (System.UInt32)a==(System.UInt32)b;
					case TypeCode.UInt64:
						return (System.UInt32)a==(System.UInt64)b;
					default:
						return false;
				}
				case TypeCode.UInt16:
				switch (bType) {
					case TypeCode.Int32:
						return (System.UInt16)a==(int)b;
					case TypeCode.Single:
						return (System.UInt16)a==(float)b;
					case TypeCode.Double:
						return (System.UInt16)a==(double)b;
					case TypeCode.Byte:
						return (System.UInt16)a==(byte)b;
					case TypeCode.Decimal:
						return (System.UInt16)a==(decimal)b;
					case TypeCode.Int16:
						return (System.UInt16)a==(System.Int16)b;
					case TypeCode.Int64:
						return (System.UInt16)a==(System.Int64)b;
					case TypeCode.SByte:
						return (System.UInt16)a==(System.SByte)b;
					case TypeCode.UInt16:
						return (System.UInt16)a==(System.UInt16)b;
					case TypeCode.UInt32:
						return (System.UInt16)a==(System.UInt32)b;
					case TypeCode.UInt64:
						return (System.UInt16)a==(System.UInt64)b;
					default:
						return false;
				}
				case TypeCode.UInt64:
				switch (bType) {
					case TypeCode.Int32:
						return (int)b>=0 && (System.UInt64)a==(System.UInt64)((int)b);
					case TypeCode.Single:
						return (System.UInt64)a==(float)b;
					case TypeCode.Double:
						return (System.UInt64)a==(double)b;
					case TypeCode.Byte:
						return (System.UInt64)a==(byte)b;
					case TypeCode.Decimal:
						return (System.UInt64)a==(decimal)b;
					case TypeCode.Int16:
						return (System.Int16)b>=0 && (System.UInt64)a==(System.UInt64)((System.Int16)b);
					case TypeCode.Int64:
						return (System.Int64)b>=0 && (System.Int64)((System.UInt64)a)==(System.Int64)b;
					case TypeCode.SByte:
						return (System.SByte)b>=0 && (System.UInt64)a==(System.UInt64)((System.SByte)b);
					case TypeCode.UInt16:
						return (System.UInt64)a==(System.UInt16)b;
					case TypeCode.UInt32:
						return (System.UInt64)a==(System.UInt32)b;
					case TypeCode.UInt64:
						return (System.UInt64)a==(System.UInt64)b;
					default:
						return false;
				}				
				
				case TypeCode.Char:
					return aType==bType && ((char)a == (char)b);
				case TypeCode.String:
					return aType==bType && ((string)a == (string)b);
				case TypeCode.Boolean:
					return aType==bType && ((bool)a == (bool)b);
				default:
					//return a==b;
					return VectorSpace.Equals(a, b);
			}
		}

		public static void WarnRedefiningGeneric(GlobalVariable g) {
			if (MessageBox.Show("You are redefining "+g.name.name+", which is a generic procedure, to be a normal procedure.\nThis will erase all your methods and prevent you from defining methods on it.\nProceed?",
				"Redefining generic procedure", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation) == DialogResult.Cancel)
				throw new Exception("Redefinition aborted.");
		}

		public static object UnwindProtect(Procedure thunk, Procedure cleanup) {
			object retval=null;
			EvalStack stack = EvalStack.CurrentThreadEvalStack;
#if UnwindProtectRestoresStack
			EvalStack.Mark stackMark = new EvalStack.Mark(stack);
#endif
			try {
				retval = Procedure.TracedCall(thunk, stack);
			} finally {
#if UnwindProtectRestoresStack
				stackMark.ResetToMark();
#endif
				EvalStack.Mark cleanupMark = new EvalStack.Mark(stack);
				stack.Push("<UnwindProtect cleanup>", null);
				Procedure.TracedCall(cleanup,stack);
				cleanupMark.ResetToMark();
			}
			return retval;
		}

		public static object Lock(object ob, Procedure thunk) {
			object result;
			lock (ob) {
				result = Procedure.TracedCall(thunk, EvalStack.CurrentThreadEvalStack);
			}
			return result;
		}

		delegate object TracedApplyDelegate(Procedure p, object[] args);
		public static object ApplyInThreadOf(System.Windows.Forms.Control f, Procedure p, object[] args) {
			return f.Invoke(new TracedApplyDelegate(Procedure.TracedApply), new object[] {p, args});
		}
		public static object AsyncApplyInThreadOf(System.Windows.Forms.Control f, Procedure p, object[] args) {
			return f.BeginInvoke(new TracedApplyDelegate(Procedure.TracedApply), new object[] {p, args});
		}

		public static object IgnoreErrors(Procedure thunk) {
			object retval = null;
			EvalStack stack = EvalStack.CurrentThreadEvalStack;
			EvalStack.Mark stackMark = new EvalStack.Mark(stack);
			try {
				retval = Procedure.TracedCall(thunk, stack);
			} catch (Exception e) {
				stackMark.ResetToMark();
				retval = e;
			}
			return retval;
		}

		public static object Catch(Procedure thunk, Type exceptionClass, Procedure handler) {
			object retval=null;
			EvalStack stack = EvalStack.CurrentThreadEvalStack;
			EvalStack.Mark stackMark = new EvalStack.Mark(stack);

			try {
				retval = Procedure.TracedCall(thunk, stack);
			} catch (Exception e) {
				if (exceptionClass.IsInstanceOfType(e)) {
					stackMark.ResetToMark();
					retval = Procedure.TracedCall(handler, stack, e);
				} else
					throw;
			}
			return retval;
		}

		public static object WithEscape(Procedure proc) {
			Escape e = new Escape();
			object retval = null;
			EvalStack stack = EvalStack.CurrentThreadEvalStack;
			EvalStack.Mark stackMark = new EvalStack.Mark(stack);

			try {
				retval = Procedure.TracedCall(proc, stack, e);
			} catch (Escape.Exception x) {
				if (x == e.exception) {
					stackMark.ResetToMark();
					retval = e.returnValue;
				} else
					throw;
			}
			return retval;
		}

		class MetaThreadStart {
			Procedure thunk;
			Fluid.BindingGroup chain;
			public MetaThreadStart(Procedure thunk, Fluid.BindingGroup chain) {
				this.thunk = thunk;
				this.chain = chain;
			}

			public void Run() {
				//Meta.EvalStack.Push("Start thread", new object[] {thunk});
				Fluid.bindingChain = chain;
				Meta.TopLevel.CurrentThreadTopLevel.CallWithErrorHandler(thunk);
			}
		}

		public static Thread Spawn(Procedure thunk) {
			Thread th = new Thread(new ThreadStart(new MetaThreadStart(thunk, Fluid.bindingChain).Run));
			th.Start();
			return th;
		}
	}
}
